<?php

namespace library\Router;

/**
 * <p>klasa wybiera na podstawie adresu URL odpowiedni kontroller i dopasowuje odpowiednią akcje</p>
 *
 * @author Damian Jóźwiak
 */
class Router extends \library\Base {

    /**
     * @readwrite
     */
    protected $url;

    /**
     * @readwrite 
     */
    protected $extension;

    /**
     * @read
     */
    protected $controller;

    /**
     * @read 
     */
    protected $action;
    protected $routes = array();
    /**
     * <p>funkcja dodaje trasę do routera</p>
     * @param \library\Router\Route $route <p>trasa do routowania</p>
     * @return \library\Router\Router
     */
    public function addRoute(Route $route) {
        $this->routes[] = $route;
        return $this;
    }
    /**
     * <p>funkcja usuwa trasę z routera</p>
     * @param \library\Router\Route $route <p>trasa do usunięcia</p>
     * @return \library\Router\Router
     */
    public function removeRoute(Route $route) {
        foreach ($this->routes as $i => $stored) {
            if ($stored == $route) {
                unset($this->routes[$i]);
            }
        }
        return $this;
    }
    /**
     * <p>funkcja zwraca tablicę tras routera, kluczem tablicy jest wzorzec trasy</p>
     * @return \library\Router\Route[];
     */
    public function getRoutes() {
        $list = array();
        foreach ($this->routes as $route) {
            $list[$route->pattern] = get_class($route);
        }
        return $list;
    }
    /**
     * <p>funkcja sprawdza czy $controller posiada metodę $action</p>
     * @param string $controller <p>nazwa kontrolera, który ma zostać wywołany</p>
     * @param type $action <p>akcja kontrolera</p>
     * @param type $parameters
     * @throws \controller\exceptions\ExceptionControllerNotFound
     * @throws \library\exceptions\ExceptionMethodNotImplemented
     */
    protected function _pass($controller, $action, $parameters = array()) {
        $name = "\controller\\".ucfirst($controller);
        $this->controller = $controller;
        $this->action = $action;
        if (!class_exists($name)){
            throw new \controller\exceptions\ExceptionControllerNotFound("kontroler {$controller} nie istnieje");
        }
           $instance = new $name(array(
                "parameters" => $parameters
            ));
            \library\Registry::set("controler", $instance);
                if (!method_exists($instance, $action)) {
            $instance->willRenderLayoutView = false;
            $instance->willRenderActionView = false;
            $message = "Kontroler ".$controller." nie posiada akcji {$action}";
            throw new \library\exceptions\ExceptionMethodNotImplemented($message);
        }
        $inspector = new \library\Inspector($instance); //nie powinno działać
        $methodMeta = $inspector->getMethodyMeta($action);
        if (!empty($methodMeta["@protected"]) || !empty($methodMeta["@private"])) {
            throw new \library\exceptions\ExceptionMethodNotImplemented($action);
        }
        $hooks = function ($meta, $type) use ($inspector, $instance) {
            if (isset($meta[$type])) {
                $run = array();
                foreach ($meta[$type] as $method) {
                    $hookMeta = $inspector->getMethodyMeta($method);
                    if (in_array($method, $run) && !empty($hookMeta["@once"])) {
                        continue;
                    }
                    $instance->$method();
                    $run [] = $method;
                }
            }
        };
        $hooks($methodMeta,"@before");
        call_user_func_array(array(
            $instance,
            $action
        ), is_array($parameters)? $parameters:array());
        $hooks($methodMeta,"@after");
        \library\Registry::erase("controller");
    }
    /**
     * <p>funkcja próbuje dopasować odpowiednią akcję kontrolera</p>
     * 
     * <p>w przypadku gdy kontroler nie zostanie podany brany jest kontroler domyślny, <br>
     * czyli klasa Index z metodą index. Jeśli do adresu url pasuje kilka kontrolerów brany jest <br>
     * pierwszy w kolejności dodania</p>
     */
    public function dispatch(){
        $url = $this->url;
        $parameters = array();
        $controller = "home";
        $action = "index";
        foreach($this->routes as $route){

            $matches = $route->matches($url);
            if ($matches){

                $controller =$route->controller;
                $action = $route->action;
                $parameters = $route->parameters;
                $this->_pass($controller, $action, $parameters);
                return;                
            }
        }
        $parts = explode("/",trim($url,"/"));
        if(sizeof($parts)>0){
            $controller = $parts[0];
            if (sizeof($parts)>=2){
                $action=$parts[1];
                $parameters=  array_slice($parts,2);
            }
        }
        $this->_pass($controller, $action, $parameters);
    }
}
